package ca.scotthyndman.as2haxe.dom;

import java.util.ArrayList;
import java.util.List;

/**
 * Represents a single identifier.
 */
public class ASSimpleName extends ASName {

	/**
	 * The "identifier" structural property of this node type.
	 * 
	 * @since 3.0
	 */
	public static final ASSimplePropertyDescriptor IDENTIFIER_PROPERTY = new ASSimplePropertyDescriptor(
			ASSimpleName.class, "identifier", String.class, true);

	/**
	 * A list of property descriptors (element type:
	 * {@link ASPropertyDescriptor}), or null if uninitialized.
	 * 
	 * @since 3.0
	 */
	private static final List<ASPropertyDescriptor> PROPERTY_DESCRIPTORS;

	static {
		List<Object> propertyList = new ArrayList<Object>(2);
		createPropertyList(ASSimpleName.class, propertyList);
		addProperty(IDENTIFIER_PROPERTY, propertyList);
		PROPERTY_DESCRIPTORS = reapPropertyList(propertyList);
	}

	/**
	 * Returns a list of structural property descriptors for this node type.
	 * Clients must not modify the result.
	 * 
	 * @return a list of property descriptors (element type:
	 *         {@link ASPropertyDescriptor})
	 * @since 3.0
	 */
	public static List<ASPropertyDescriptor> propertyDescriptors(int apiLevel) {
		return PROPERTY_DESCRIPTORS;
	}

	//
	// ======== MEMBERS
	//

	private String identifier;

	//
	// ======== CONSTRUCTION
	//

	public ASSimpleName(AST ast) {
		super(ast);
	}

	public ASSimpleName(AST ast, String name) {
		this(ast);
		setIdentifier(name);
	}

	//
	// ======== GETTERS / SETTERS
	//

	/**
	 * Returns this node's identifier.
	 * 
	 * @return the identifier of this node
	 */
	public String getIdentifier() {
		return identifier;
	}

	/**
	 * Sets the identifier of this node to the given value. The identifier
	 * should be legal according to the rules of the language. Note that
	 * keywords are not legal identifiers.
	 * 
	 * @param identifier
	 *            the identifier of this node
	 * @exception IllegalArgumentException
	 *                if the identifier is invalid
	 */
	public void setIdentifier(String identifier) {
		if (identifier == null) {
			throw new IllegalArgumentException();
		}

		if (ASReservedWords.isReservedWord(identifier)) {
			throw new IllegalArgumentException();
		}

		preValueChange(IDENTIFIER_PROPERTY);
		this.identifier = identifier;
		postValueChange(IDENTIFIER_PROPERTY);
	}

	//
	// ======== INTERNAL GETTERS / SETTERS
	//

	/*
	 * (omit javadoc for this method) Method declared on ASTNode.
	 */
	final Object internalGetSetObjectProperty(
			ASSimplePropertyDescriptor property, boolean get, Object value) {
		if (property == IDENTIFIER_PROPERTY) {
			if (get) {
				return getIdentifier();
			} else {
				setIdentifier((String) value);
				return null;
			}
		}
		// allow default implementation to flag the error
		return super.internalGetSetObjectProperty(property, get, value);
	}

	//
	// ======== HELPERS
	//

	/**
	 * Returns whether this simple name represents a name that is being defined,
	 * as opposed to one being referenced.
	 * <p>
	 * Note that this is a convenience method that simply checks whether this
	 * node appears in the declaration position relative to its parent. It
	 * always returns <code>false</code> if this node is unparented.
	 * </p>
	 * 
	 * @return <code>true</code> if this node declares a name, and
	 *         <code>false</code> otherwise
	 */
	public boolean isDeclaration() {
		ASPropertyDescriptor d = getLocationInParent();
		if (d == null) {
			// unparented node
			return false;
		}
		ASTNode parent = getParent();
		if (parent instanceof ASTypeDeclaration) {
			return (d == ASTypeDeclaration.NAME_PROPERTY);
		}
		if (parent instanceof ASFunction) {
			ASFunction p = (ASFunction) parent;
			// could be the name of the method or constructor
			return !p.isConstructor() && (d == ASFunction.NAME_PROPERTY);
		}
		if (parent instanceof ASVariableDeclaration) {
			return (d == ASVariableDeclaration.NAME_PROPERTY);
		}

		return false;
	}

	// ====== TO STRING, EQUALS AND CLONE

	@Override
	public String toString() {
		return getIdentifier();
	}

	@Override
	public Object clone() {
		ASSimpleName clone = new ASSimpleName(getAST());
		clone.setIdentifier(getIdentifier());
		return clone;
	}

	@Override
	public boolean equals(Object obj) {
		if (obj == null || !(obj instanceof ASSimpleName)) {
			return false;
		}

		return getIdentifier().equals(((ASSimpleName) obj).getIdentifier());
	}
}
